/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tbruyelle.rxpermissions2;

import java.util.*;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;
import io.reactivex.subjects.PublishSubject;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.ability.fraction.FractionManager;

public class RxPermissions {
    static final String TAG = RxPermissions.class.getSimpleName();
    /**
     * 用于发起Rx数据源的空对象
     */
    private static final Object TRIGGER = new Object();

    /**
     * 权限申请代理Fraction懒加载和缓存
     */
    Lazy<RxPermissionsFraction> mRxPermissionsFraction;

    /**
     * 以FractionAbility，构造实例
     */
    public RxPermissions(@NonNull final FractionAbility ability) {
        mRxPermissionsFraction = getLazySingleton(ability.getFractionManager());
    }

    /**
     * 获取懒加载实例
     *
     * @param fractionManager Fraction管理器
     */
    @NonNull
    private Lazy<RxPermissionsFraction> getLazySingleton(@NonNull final FractionManager fractionManager) {
        return new Lazy<RxPermissionsFraction>() {
            private RxPermissionsFraction rxPermissionsFraction;

            @Override
            public synchronized RxPermissionsFraction get() {
                //缓存实例，下次使用直接获取
                if (rxPermissionsFraction == null) {
                    rxPermissionsFraction = getRxPermissionsFraction(fractionManager);
                }
                return rxPermissionsFraction;
            }
        };
    }

    /**
     * 代理申请结果回调处理
     */
    public void onRequestPermissionsFromUserResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        mRxPermissionsFraction.get().onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
    }

    /**
     * 获取代理Fraction
     *
     * @param fractionManager Fraction管理器
     */
    private RxPermissionsFraction getRxPermissionsFraction(@NonNull final FractionManager fractionManager) {
        //查找Fraction实例
        RxPermissionsFraction rxPermissionsFraction = findRxPermissionsFraction(fractionManager);
        boolean isNewInstance = rxPermissionsFraction == null;
        //没有找到则创建，再添加
        if (isNewInstance) {
            rxPermissionsFraction = new RxPermissionsFraction();
            fractionManager
                    .startFractionScheduler()
                    //不需要添加到布局中，直接传0
                    .add(0, rxPermissionsFraction, TAG)
                    .submit();
        }
        return rxPermissionsFraction;
    }

    /**
     * 查找权限代理Fraction
     *
     * @param fractionManager Fraction管理器
     */
    private RxPermissionsFraction findRxPermissionsFraction(@NonNull final FractionManager fractionManager) {
        Optional<Fraction> optionalFraction = fractionManager.getFractionByTag(TAG);
        return (RxPermissionsFraction) optionalFraction.orElse(null);
    }

    /**
     * 设置Log打印
     *
     * @param logging 是否打印Log
     */
    public void setLogging(boolean logging) {
        mRxPermissionsFraction.get().setLogging(logging);
    }

    /**
     * 批量申请权限Transformer，可以使用compose操作符连接，全部都授权了才返回true，否则为false，只会通知订阅者一次
     *
     * @param permissions 需要申请的权限
     */
    @SuppressWarnings("WeakerAccess")
    public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
        return new ObservableTransformer<T, Boolean>() {
            @Override
            public ObservableSource<Boolean> apply(Observable<T> o) {
                //申请权限
                return request(o, permissions)
                        //一次性申请，buffer指定一次发射的数量为权限列表数量，所以是一次性申请权限
                        .buffer(permissions.length)
                        .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {
                            @Override
                            public ObservableSource<Boolean> apply(List<Permission> permissions) {
                                //申请的权限为空，直接通知订阅者的onComplete
                                if (permissions.isEmpty()) {
                                    return Observable.empty();
                                }
                                //所有权限都允许了，才返回true
                                for (Permission permission : permissions) {
                                    //是要有一个没有授权，则返回false
                                    if (!permission.granted) {
                                        return Observable.just(false);
                                    }
                                }
                                //所有都允许了
                                return Observable.just(true);
                            }
                        });
            }
        };
    }

    /**
     * 申请权限Transformer，可以使用compose操作符连接，每个申请一次，所以会调用订阅者多次
     *
     * @param permissions 申请的权限列表
     */
    @SuppressWarnings("WeakerAccess")
    public <T> ObservableTransformer<T, Permission> ensureEach(final String... permissions) {
        return new ObservableTransformer<T, Permission>() {
            @Override
            public ObservableSource<Permission> apply(Observable<T> o) {
                return request(o, permissions);
            }
        };
    }

    /**
     * 和ensure类似，都是批量申请权限，但是返回结果不是Boolean，而是Permission对象
     *
     * @param permissions 申请的权限列表
     */
    public <T> ObservableTransformer<T, Permission> ensureEachCombined(final String... permissions) {
        return new ObservableTransformer<T, Permission>() {
            @Override
            public ObservableSource<Permission> apply(Observable<T> o) {
                //申请权限
                return request(o, permissions)
                        //buffer，一次性批量申请所有权限
                        .buffer(permissions.length)
                        .flatMap(new Function<List<Permission>, ObservableSource<Permission>>() {
                            @Override
                            public ObservableSource<Permission> apply(List<Permission> permissions) {
                                if (permissions.isEmpty()) {
                                    return Observable.empty();
                                }
                                //将结果直接发送
                                return Observable.just(new Permission(permissions));
                            }
                        });
            }
        };
    }

    /**
     * 直接发起申请权限，批量申请权限
     *
     * @param permissions 申请的权限列表
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensure(permissions));
    }

    /**
     * 直接发起申请权限，直接发起，每个权限都申请一次，会调用多次订阅者
     *
     * @param permissions 申请的权限列表
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Permission> requestEach(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensureEach(permissions));
    }

    /**
     * 直接发起申请权限，也是批量申请，但是返回结果不是Boolean而是Permission
     *
     * @param permissions 申请的权限列表
     */
    public Observable<Permission> requestEachCombined(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensureEachCombined(permissions));
    }

    /**
     * 申请权限request中转
     *
     * @param trigger     原始数据源
     * @param permissions 申请的权限
     */
    private Observable<Permission> request(final Observable<?> trigger, final String... permissions) {
        if (permissions == null || permissions.length == 0) {
            throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");
        }
        //数据源一一匹配，确保是成对存在
        return oneOf(trigger, pending(permissions))
                .flatMap(new Function<Object, Observable<Permission>>() {
                    @Override
                    public Observable<Permission> apply(Object o) {
                        //真正申请权限的实现，每个权限都一个个去申请
                        return requestImplementation(permissions);
                    }
                });
    }

    /**
     * 过滤掉权限和结果数据源不匹配的情况
     *
     * @param permissions 申请的权限
     */
    private Observable<?> pending(final String... permissions) {
        for (String permission : permissions) {
            if (!mRxPermissionsFraction.get().containsByPermission(permission)) {
                return Observable.empty();
            }
        }
        return Observable.just(TRIGGER);
    }

    /**
     * 数据源一一匹配，确保是成对存在
     */
    private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
        if (trigger == null) {
            return Observable.just(TRIGGER);
        }
        return Observable.merge(trigger, pending);
    }

    /**
     * 真正申请权限
     *
     * @param permissions 申请的权限
     */
    private Observable<Permission> requestImplementation(final String... permissions) {
        //权限申请前的结果
        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        //待申请的权限列表
        List<String> unrequestedPermissions = new ArrayList<>();
        //为每个权限创建一个数据源
        for (String permission : permissions) {
            mRxPermissionsFraction.get().log("Requesting permission " + permission);
            //加入已经被允许的权限数据源
            if (isGranted(permission)) {
                list.add(Observable.just(new Permission(permission, true, false)));
                continue;
            }
            //加入被拒绝的权限的数据源
            if (isRevoked(permission)) {
                list.add(Observable.just(new Permission(permission, false, false)));
                continue;
            }
            //获取权限申请存根，这种是为了避免快速请求多次，存入了多个结果数据源回调
            PublishSubject<Permission> subject = mRxPermissionsFraction.get().getSubjectByPermission(permission);
            //不存在则创建一个，并保存到代理Fraction
            if (subject == null) {
                //需要申请，添加到待申请的权限
                unrequestedPermissions.add(permission);
                subject = PublishSubject.create();
                mRxPermissionsFraction.get().setSubjectForPermission(permission, subject);
            }
            //加入待进行申请的权限数据源
            list.add(subject);
        }
        //如果存在需要申请的权限，则申请权限
        if (!unrequestedPermissions.isEmpty()) {
            //集合转为数组
            String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
            //调用代理Fraction去申请权限
            requestPermissionsFromFraction(unrequestedPermissionsArray);
        }
        //发射允许和被拒绝的权限，concat顺序发送权限结果数据源，将他们的结果按顺序铺平发送
        return Observable.concat(Observable.fromIterable(list));
    }

    /**
     * 调用申请权限的代理Fraction申请权限
     *
     * @param permissions 目标权限列表
     */
    void requestPermissionsFromFraction(String[] permissions) {
        mRxPermissionsFraction.get().log("requestPermissionsFromFraction " + join(", ", Arrays.asList(permissions)));
        mRxPermissionsFraction.get().requestPermissions(permissions);
    }

    /**
     * 判断权限是否允许
     *
     * @param permission 目标权限
     */
    @SuppressWarnings("WeakerAccess")
    public boolean isGranted(String permission) {
        return mRxPermissionsFraction.get().isGranted(permission);
    }

    /**
     * 权限是否拒绝
     *
     * @param permission 目标权限
     */
    @SuppressWarnings("WeakerAccess")
    public boolean isRevoked(String permission) {
        return mRxPermissionsFraction.get().isRevoked(permission);
    }

    /**
     * 懒加载接口
     */
    @FunctionalInterface
    public interface Lazy<V> {
        /**
         * 获取懒加载的对象
         *
         * @return 缓存的对象
         */
        V get();
    }

    private String join(CharSequence delimiter, Iterable tokens) {
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = tokens.iterator();
        if (it.hasNext()) {
            sb.append(it.next());
            while (it.hasNext()) {
                sb.append(delimiter);
                sb.append(it.next());
            }
        }
        return sb.toString();
    }
}